﻿using GpuHud;
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;

//Idx*100 + ParentIdx 
//ParentIdx代表的是第几个DrawCall(Texture)
//Idx代表的是一个DrawCall中的第几个Patch
//每次Draw之前,重新分配Idx
public unsafe class HudDataTextureCollection<T> : StyleDataCollectionBase<T> where T : struct, StructDataTag
{
    struct NativeTexture
    {
        public Texture2D texture;
        public NativeArray<float> nativeData;
        public byte* ptr;
    }

    #region Texture数据
    private List<NativeTexture> renderDataTexture = new List<NativeTexture>(1);
    private NativeList<bool> textureFrameDirty = new NativeList<bool>(2, Allocator.Persistent);
    private List<MaterialPropertyBlock> perDrawMatBlock = new List<MaterialPropertyBlock>();

    //GpuInstance时的下标重映射
    private NativeArray<int> instanceIdx = new NativeArray<int>(2, Allocator.Persistent);
    #endregion

    #region 缓存数据
    private NativeList<Color> cacheRangeData;
    private NativeList<Color> tempRangeData;
    //每个实体的ShaderData需要几个RGBA float数据
    private int perShaderDataNeedsColor;
    //每个实体的一个Batch需要多少Color
    private int perBatchCount;
    //一个Batch要多少字节
    private int perBatchStride;
    //一个Block的Batch
    private int perBlockBatchCount;
    //一个DrawCall最多可以画多少个Batch
    private static int MaxBatchPerDrawCall = 500;
    private static float InvMaxBatchPerDrawCall = 1.0f / 500;
    private Matrix4x4[] matrices;
    private float[] instanceIds;
    //float 字节大小
    private int floatSizeof;
    #endregion


    public override int OnScreenArrayLength => m_OnScreenCount;

    public override int OnScreenStyleCount => m_OnScreenCount / m_BlockCount;

    public override int OffScreenStyleCount => TotalCount - OnScreenStyleCount;

    public override int TotalCount => m_PoolAccessors.Count;

    private int m_OnScreenCount;

    //因为DrawInstance实际上每个Draw只支持500,为了增加每个Drawcall可以绘制的数量,
    //会在绘制前将Batch个相同的Mesh合并成一个Mesh,大模型不能合并,文字这种小模型无所谓
    //而且可以在模型的颜色中加入一些信息,比如 r 代表当前Mesh在Batch中的偏移
    //g/b信息还未定义,且需要对于所有的Mesh都具有相同的定义
    protected override Mesh m_RenderMesh
    {
        get
        {
            return BatchedMesh;
        }
    }

    protected Mesh BatchedMesh;

    /// <summary>
    /// shaderData长度，默认为1
    /// </summary>
    /// <param name="blockCount"></param>
    public HudDataTextureCollection(int stride, int blockCount = 1) : base(stride, blockCount)
    {
        //1. 生成PatchData
        perShaderDataNeedsColor = Mathf.CeilToInt(1.0f * Ttype.GetParamsCount() / 4);
        perBatchCount = perShaderDataNeedsColor * blockCount;
        perBatchStride = perBatchCount * UnsafeUtility.SizeOf<Color>();
        cacheRangeData = new NativeList<Color>(perBatchCount, Allocator.Persistent);
        tempRangeData = new NativeList<Color>(perBatchCount, Allocator.Persistent);
        cacheRangeData.Length = perBatchCount;
        tempRangeData.Length = perBatchCount;
        //初始化Mesh和Mat信息
        InitMatParams();
        //初始化参数池
        InitRenderPool(0);
        //初始化BatchMesh
        InitBatchMesh();
    }

    public override void Dispose()
    {
        //ArgsBuffers.Dispose();
        if (cacheRangeData.IsCreated)
            cacheRangeData.Dispose();
        if (tempRangeData.IsCreated)
            tempRangeData.Dispose();
        if (textureFrameDirty.IsCreated)
            textureFrameDirty.Dispose();
        if (instanceIdx.IsCreated)
            instanceIdx.Dispose();
    }

    public override void OnDestroy()
    {
        if (cacheRangeData.IsCreated)
            cacheRangeData.Dispose();
        if (tempRangeData.IsCreated)
            tempRangeData.Dispose();
        if (textureFrameDirty.IsCreated)
            textureFrameDirty.Dispose();
        if (instanceIdx.IsCreated)
            instanceIdx.Dispose();
    }

    public override void Draw(Material material, Texture texture, bool guiDraw)
    {
        if (!SystemInfo.supportsInstancing)
        {
            return;
        }
        if (!guiDraw)
        {
            ReSwapShaderData();
            //如果Texture发生改变,需要调用Apply()
        }
        if (OnScreenArrayLength <= 0)
            return;

        int consumeCount = m_OnScreenCount;
        for (int i = 0; i < perDrawMatBlock.Count; ++i)
        {
            if (consumeCount <= 0) break;
            for (int j = 0; j < MaxBatchPerDrawCall; ++j)
            {
                int target = i * MaxBatchPerDrawCall + j;
                if (target < m_PoolAccessors.Count && m_PoolAccessors[target] != null)
                {
                    matrices[j] = Matrix4x4.TRS(m_PoolAccessors[target].m_dataAccessor.CurrentTextPosition, Quaternion.identity, Vector3.one);
                }
            }
            perDrawMatBlock[i].SetTexture(CollectionShaderTag.MainTex, texture);
     
            Graphics.DrawMeshInstanced(
                m_RenderMesh, 0, 
                material, matrices, 
                Mathf.Min(consumeCount, MaxBatchPerDrawCall), perDrawMatBlock[i], 
                ShadowCastingMode.Off, false,
                GpuHudFacade.GpuFacade.drawLayer, GpuHudFacade.GpuFacade.HudCamera
            );
            consumeCount -= MaxBatchPerDrawCall;
        }
    }

    public unsafe override T GetStyle(int realIdx)
    {
        int targetBatch = Mathf.FloorToInt(1.0f * realIdx / m_BlockCount);
        int targetTexture = GetTextureDataIdx(targetBatch);
        int textureBatch = GetTextureBatchIdx(targetBatch);
        int innerBatch = GetInnerBatchIdx(targetBatch);
        byte* originPtr = renderDataTexture[targetTexture].ptr;
        //按perShaderDataNeedsColor的Color占4个float来算，所有数据都是float,按4字节对齐(是否会出现32位机器的问题)
        int l1 = (textureBatch * perBatchCount + innerBatch) * UnsafeUtility.SizeOf<Color>();

        return UnsafeUtility.ReadArrayElement<T>((originPtr + l1), 0);
    }

    public override void SetOffScreen(int idx)
    {
    }

    public override void SetOnScreen(int idx)
    {
    }

    public override void SetColor(int idx, Color color)
    {
        int localIdx = Ttype.GetColorOffset();
        if (localIdx != -1)
        {
            int textureIdx = GetTextureDataIdx(idx);
            int batchIdx = GetTextureBatchIdx(idx);
            //找到第一个color的位置
            int floatIdx = batchIdx * perBatchCount * 4;
            NativeArray<float> origin = renderDataTexture[textureIdx].nativeData;
            for (int i = 0; i < m_BlockCount; ++i)
            {
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx] = color.r;
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx + 1] = color.g;
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx + 2] = color.b;
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx + 3] = color.a;
            }
            textureFrameDirty[textureIdx] = true;
        }
    }
    public override void SetAlpha(int idx, float alpha)
    {
        int localIdx = Ttype.GetColorOffset();
        if (localIdx != -1)
        {
            int textureIdx = GetTextureDataIdx(idx);
            int batchIdx = GetTextureBatchIdx(idx);
            //找到第一个color的位置
            int floatIdx = batchIdx * perBatchCount * 4;
            NativeArray<float> origin = renderDataTexture[textureIdx].nativeData;
            for (int i = 0; i < m_BlockCount; ++i)
            {
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx + 3] = alpha;
            }
            textureFrameDirty[textureIdx] = true;
        }
    }
    public override void SetPosition(int idx, float3 position)
    {
        int textureIdx = GetTextureDataIdx(idx);
        textureFrameDirty[textureIdx] = true;
        /*int localIdx = Ttype.GetPositionOffset();
        if (localIdx != -1)
        {
            int textureIdx = GetTextureDataIdx(idx);
            int batchIdx = GetTextureBatchIdx(idx);
            //找到第一个color的位置
            int floatIdx = batchIdx * perBatchCount * 4;
            NativeArray<float> origin = renderDataTexture[textureIdx].nativeData;
            for (int i = 0; i < m_BlockCount; ++i)
            {
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx] = position.x;
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx + 1] = position.y;
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx + 2] = position.z;
            }
            textureFrameDirty[textureIdx] = true;
        }*/
        //GpuInstance的特性不需要去更新ShaderData中的Position,只需要设置转换矩阵即可
    }

    public override void SetScale(int idx, float scale)
    {
        int localIdx = Ttype.GetScaleOffset();
        if (localIdx != -1)
        {
            int textureIdx = GetTextureDataIdx(idx);
            int batchIdx = GetTextureBatchIdx(idx);
            //找到第一个color的位置
            int floatIdx = batchIdx * perBatchCount * 4;
            NativeArray<float> origin = renderDataTexture[textureIdx].nativeData;
            for (int i = 0; i < m_BlockCount; ++i)
            {
                origin[floatIdx + i * perShaderDataNeedsColor * 4 + localIdx] = scale;
            }
            textureFrameDirty[textureIdx] = true;
        }
    }

    /// <summary>
    /// realIdx 是 Batch + batch内偏移
    /// </summary>
    /// <param name="data"></param>
    /// <param name="realIdx"></param>
    public unsafe override void SetStyle(T data, int realIdx)
    {
        //1. 找到batch
        //2. 找到图
        //3. 填数据
        int targetTexture = GetTextureDataIdx(realIdx);
        int textureBatch = GetTextureBatchIdx(realIdx);
        int innerBatch = GetInnerBatchIdx(realIdx);
        byte* originPtr = renderDataTexture[targetTexture].ptr;
        //按perShaderDataNeedsColor的Color占4个float来算，所有数据都是float,按4字节对齐
        int l1 = (textureBatch * perBatchCount + innerBatch * perShaderDataNeedsColor) * UnsafeUtility.SizeOf<Color>();
        UnsafeUtility.WriteArrayElement((originPtr + l1), 0, data);
        textureFrameDirty[targetTexture] = true;
    }

    //返回的实际上是第几个Batch
    protected override int OnAddStyleShaderData(T data, GpuHudStyleDataAccessor<T> accessor)
    {
        int idx = TotalCount;
        //1. 先选位置
        //实际插入位置在最后一个的后面(同ShaderData的选择)
        int targetTextureIdx = Mathf.FloorToInt(1.0f * idx * InvMaxBatchPerDrawCall);
        if (targetTextureIdx == renderDataTexture.Count)
        {
            InitRenderPool(renderDataTexture.Count);
        }
        InitialShaderData(idx * m_BlockCount);
        //下标策略
        // 目标图下标 + 目标batch下标 + batch内下标
        //图的下标 = (idx/BlockCount)/MAXBatchPerDrawCall
        //目标batch下标 = idx/BlockCount
        //batch内下标 = idx + 偏移
        return idx * m_BlockCount;
    }

    public override void InitialShaderData(int idx)
    {
        int targetTexture = GetTextureDataIdx(idx);
        int targetBatchIdx = GetTextureBatchIdx(idx);
        byte* originPtr = renderDataTexture[targetTexture].ptr;
        byte* cachePtr = (byte*)cacheRangeData.GetUnsafeList()->Ptr;
        int l1 = targetBatchIdx * perBatchStride;
        //3. 数据从缓冲区拷贝到j
        UnsafeUtility.MemCpy(originPtr + l1, cachePtr, perBatchStride);
        textureFrameDirty[targetTexture] = true;
    }

    #region 数据交换策略

    /// <summary>
    /// 是按照Batch来Swap的
    /// </summary>
    /// <param name="i"></param>
    /// <param name="j"></param>
    public unsafe void SwapShaderData(int i, int j)
    {
        if (i == j || i < 0 || j < 0)
            return;

        //1. 先找到i和j的TextureData
        //2. 找到各自的组内指针
        //3. 交换

        int textureIdxI = GetTextureDataIdx(i);
        int textureIdxJ = GetTextureDataIdx(j);

        int batchIdxI = GetTextureBatchIdx(i);
        int batchIdxJ = GetTextureBatchIdx(j);

        int j1 = batchIdxJ * perBatchStride;
        int i1 = batchIdxI * perBatchStride;
        byte* originPtr = renderDataTexture[textureIdxI].ptr;
        byte* targetPtr = renderDataTexture[textureIdxJ].ptr;
        byte* tempPtr = (byte*)tempRangeData.GetUnsafeList()->Ptr;
        //1. 数据从i拷贝到缓冲区
        UnsafeUtility.MemCpy(tempPtr, originPtr + i1, perBatchStride);
        //2. 数据从目标位置拷贝到i
        UnsafeUtility.MemCpy(originPtr + i1, targetPtr + j1, perBatchStride);
        //3. 数据从缓冲区拷贝到j
        UnsafeUtility.MemCpy(targetPtr + j1, tempPtr, perBatchStride);

        textureFrameDirty[textureIdxI] = true;
        textureFrameDirty[textureIdxJ] = true;
    }

    /// <summary>
    /// 1. 交换本质上和Array形式的差不多,但是问题在于,
    /// </summary>
    int collectorPtr = 0;
    public void ReSwapShaderData()
    {
        if (m_PoolAccessors != null)
        {
            collectorPtr = 0;
            OnScreenCollector();
            m_OnScreenCount = collectorPtr;
            OffScreenCollector();
            RemoveDeleteCollector();
            //提交渲染数据变更
            SubmitTextureDataChange();
            //恢复帧缓存数据
            RecoverFrameData();
        }
    }

    #region 数据延迟Swap流程
    /// <summary>
    /// 收集在屏幕内的数据
    /// </summary>
    private void OnScreenCollector()
    {
        GpuHudStyleDataAccessor<T> tempAccessor = null;
        for (int i = m_PoolAccessors.Count - 1; ; --i)
        {
            if (i < 0) break;
            tempAccessor = m_PoolAccessors[i];
            if (tempAccessor.m_isOnScreen && !tempAccessor.m_isDelete)
            {
                FindFirstCanOnScreenCollectorIdx();
                if (collectorPtr <= i)
                {
                    SwapAccessor(i, collectorPtr);
                    SwapShaderData(m_PoolAccessors[i].m_Idx, m_PoolAccessors[collectorPtr].m_Idx);
                    collectorPtr++;
                }
                else
                {
                    return;
                }
            }
        }
    }

    private void FindFirstCanOnScreenCollectorIdx()
    {
        if (collectorPtr >= m_PoolAccessors.Count)
        {
            collectorPtr = m_PoolAccessors.Count;
            return;
        }
        GpuHudStyleDataAccessor<T> tempAccessor = m_PoolAccessors[collectorPtr];
        while (!tempAccessor.m_isDelete && tempAccessor.IsOnScreen)
        {
            collectorPtr++;
            if (collectorPtr >= m_PoolAccessors.Count)
            {
                collectorPtr = m_PoolAccessors.Count;
                return;
            }
            tempAccessor = m_PoolAccessors[collectorPtr];
        }
    }

    private void SwapAccessor(int origin, int target)
    {
        if (origin == target) return;
        GpuHudStyleDataAccessor<T> tempAccessor = m_PoolAccessors[origin];
        m_PoolAccessors[origin] = m_PoolAccessors[target];
        m_PoolAccessors[target] = tempAccessor;
        m_PoolAccessors[origin].m_Idx = origin * m_BlockCount;
        m_PoolAccessors[target].m_Idx = target * m_BlockCount;
    }

    /// <summary>
    /// 收集在屏幕外的数据
    /// </summary>
    private void OffScreenCollector()
    {
        GpuHudStyleDataAccessor<T> tempAccessor = null;
        for (int i = m_PoolAccessors.Count - 1; ; --i)
        {
            if (i < 0) break;
            tempAccessor = m_PoolAccessors[i];
            if (tempAccessor.m_isOffScreen && !tempAccessor.m_isDelete)
            {
                FindFirstCanOffScreenCollectorIdx();
                if (collectorPtr <= i)
                {
                    SwapAccessor(i, collectorPtr);
                    SwapShaderData(m_PoolAccessors[i].m_Idx, m_PoolAccessors[collectorPtr].m_Idx);
                    //SwapShaderData(i, collectorPtr);
                    collectorPtr++;
                }
                else return;

            }
        }
    }

    private void FindFirstCanOffScreenCollectorIdx()
    {
        if (collectorPtr >= m_PoolAccessors.Count)
        {
            collectorPtr = m_PoolAccessors.Count;
            return;
        }
        GpuHudStyleDataAccessor<T> tempAccessor = m_PoolAccessors[collectorPtr];
        while (!tempAccessor.m_isDelete && tempAccessor.m_isOffScreen)
        {
            collectorPtr++;
            if (collectorPtr >= m_PoolAccessors.Count)
            {
                collectorPtr = m_PoolAccessors.Count;
                return;
            }
            tempAccessor = m_PoolAccessors[collectorPtr];
        }
    }
    #endregion

    /// 删除末尾元素
    /// </summary>
    private void RemoveDeleteCollector()
    {
        if (collectorPtr >= m_PoolAccessors.Count) return;

        m_PoolAccessors.RemoveRange(collectorPtr, m_PoolAccessors.Count - collectorPtr);
    }

    /// <summary>
    /// 帧结束时恢复数据
    /// </summary>
    private void RecoverFrameData()
    {
        for (int i = 0; i < textureFrameDirty.Length; ++i)
        {
            textureFrameDirty[i] = false;
        }
    }

    /// <summary>
    /// 提交Texture数据变更
    /// </summary>
    private void SubmitTextureDataChange()
    {
        for (int i = 0; i < renderDataTexture.Count; ++i)
        {
            if (textureFrameDirty[i])
            {
                renderDataTexture[i].texture.Apply(false);
            }
        }
    }
    #endregion

    #region Idx池
    /// <summary>
    /// 对第idx个池子中的数据进行初始化
    /// 仍然考虑分布密集的做法.
    /// </summary>
    /// <param name="idx"></param>
    private void InitRenderPool(int idx)
    {
        //超过了当前已有Texture的上限了,应该按顺序分配
        if (idx != renderDataTexture.Count)
        {
            return;
        }
        void InitDataTexture()
        {
            Texture2D dataTexture = new Texture2D(perBatchCount, MaxBatchPerDrawCall, TextureFormat.RGBAFloat, 1, true);
            dataTexture.filterMode = FilterMode.Point;
            dataTexture.anisoLevel = 0;
            dataTexture.wrapMode = TextureWrapMode.Clamp;
            renderDataTexture.Add(new NativeTexture()
            {
                texture = dataTexture,
                nativeData = dataTexture.GetRawTextureData<float>(),
                ptr = (byte*)dataTexture.GetRawTextureData<float>().GetUnsafePtr()
            });
            textureFrameDirty.Add(false);
        }
        //设置需要的基础数据
        void InitMatBlock()
        {
            MaterialPropertyBlock block = new MaterialPropertyBlock();
            block.SetTexture(CollectionShaderTag.TextureData, renderDataTexture[idx].texture);
            block.SetInt(CollectionShaderTag.ColorsCountPerShaderData, perShaderDataNeedsColor);
            block.SetInt(CollectionShaderTag.TexDataHeight, MaxBatchPerDrawCall);
            block.SetInt(CollectionShaderTag.TexDataWidth, perShaderDataNeedsColor * m_BlockCount);
            block.SetFloatArray(CollectionShaderTag.InstanceArray, instanceIds);
            perDrawMatBlock.Add(block);
        }

        //实际执行
        InitDataTexture();
        InitMatBlock();
    }
    #endregion

    #region 初始化Mesh

    /// <summary>
    /// 设置BatchMesh
    /// 现在先做成死的,后面需要拆出来
    //  而且现在只需要顶点和三角形
    //  这个只会在初始化Collection时释放一次
    /// </summary>
    private void InitBatchMesh()
    {
        Color defaultColor = default;
        defaultColor.r = 0; //r通道是Batch内偏移

        Mesh mesh = new Mesh();
        Mesh originMesh = GpuHudFacade.GpuFacade.Settings.mesh;
        List<Vector3> verts = new List<Vector3>();
        List<int> triangles = new List<int>();
        List<Vector2> uvs = new List<Vector2>();
        int originVertCnt = originMesh.vertexCount;
        List<Color> colors = new List<Color>(originVertCnt * m_BlockCount);

        int originTriCnt = originMesh.triangles.Length;
        for (int i = 0; i < m_BlockCount; ++i)
        {
            verts.AddRange(originMesh.vertices);
            triangles.AddRange(originMesh.triangles);
            uvs.AddRange(originMesh.uv);
            if (i == 0)
            {
                for (int j = 0; j < originVertCnt; ++j)
                {
                    colors.Add(defaultColor);
                }
            }
            else
            {
                for (int j = i * originTriCnt; j < (i * originTriCnt + originTriCnt); ++j)
                {
                    triangles[j] += originVertCnt * i;
                }
                defaultColor.r = i;
                for (int j = 0; j < originVertCnt; ++j)
                {
                    defaultColor.g = verts[j].x;
                    defaultColor.b = verts[j].y;
                    defaultColor.a = verts[j].z;
                    colors.Add(defaultColor);
                }
            }
        }

        mesh.SetVertices(verts);
        mesh.SetTriangles(triangles, 0);
        mesh.SetColors(colors);
        mesh.SetUVs(0, uvs);

        BatchedMesh = mesh;
    }

    private void InitMatParams()
    {
        matrices = new Matrix4x4[MaxBatchPerDrawCall];

        instanceIds = new float[MaxBatchPerDrawCall];
        for (int i = 0; i < MaxBatchPerDrawCall; ++i)
        {
            instanceIds[i] = i;
        }
    }

    #endregion

    #region 工具
    /// <summary>
    /// 根据下标获取贴图数据下标
    /// </summary>
    /// <param name="originIdx"></param>
    /// <returns></returns>
    public int GetTextureDataIdx(int originIdx)
    {
        return Mathf.FloorToInt((originIdx / m_BlockCount) * InvMaxBatchPerDrawCall);
    }
    /// <summary>
    /// 根据原始下标获取在当前贴图数据的Batch下标
    /// </summary>
    /// <param name="originIdx"></param>
    /// <returns></returns>
    public int GetTextureBatchIdx(int originIdx)
    {
        return (Mathf.FloorToInt((originIdx / m_BlockCount)) % MaxBatchPerDrawCall);
    }

    /// <summary>
    /// 获取Batch组内下标
    /// </summary>
    /// <param name="originIdx"></param>
    /// <returns></returns>
    public int GetInnerBatchIdx(int originIdx)
    {
        return originIdx % m_BlockCount;
    }



    #endregion
}
